package info.niwota.home;

import info.niwota.home.fm.ShellActivity;
import instant.openvfs.home.ApplicationUtils;
import instant.openvfs.home.ClippedDrawable;
import instant.openvfs.home.Debug;
import instant.openvfs.home.Preferences;
import instant.openvfs.home.ResourceInfo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import x.java.io.File;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class HomeFlingActivity extends ShellActivity implements OnClickListener {
	private static final String TAG = "HomeFlingActivity";
	
	private static Future<File[]> result;
	private static int progress;

	private static final ProgressMonitor progressMonitor = new ProgressMonitor();

	private static final class ProgressHandler extends Handler {
		private boolean started;

		private void handleEnd() {
			started = false;
			//
			try {
				childs = result.get();
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler childs: " + childs);
			}
			if (result.isCancelled()) {
				return;
			}
			load2();
		}

		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler folder: " + file + " what: "
						+ what + " progress: " + progress);
			}
			if (what == WHAT_START) {
				handler.removeMessages(WHAT_END);
				handler.removeMessages(WHAT_PROGRRESS);
				handler.removeMessages(WHAT_START);
				//
				started = true;
				showBusy(true);
				return;
			}
			if (started) {
				//show progress
			}
			if (what == WHAT_END) {
				
				try {
					showBusy(false);
					
					handleEnd();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					//
				}
			}
		}

		@Override
		public String toString() {
			return "Message Handler";
		}
	}

	private final static ProgressHandler handler = new ProgressHandler();

	private final static int WHAT_END = 3;

	private final static int WHAT_PROGRRESS = 2;

	private final static int WHAT_START = 1;

	private final static Lister lister = new Lister();

	private static final class Lister implements Callable<File[]> {
		private File[] NONE = new File[0];

		public File[] call() throws Exception {
			if (Debug.DEBUG) {
				Log.d(TAG, "Lister folder: " + file);
			}
			File[] fl = file.listFiles();
			if (Debug.DEBUG) {
				Log.d(TAG, "Lister folder done. " + fl + " size: "
						+ (fl == null ? 0 : fl.length));
			}
			if (fl == null) {
				fl = NONE;
			}
			return fl;
		}
	}

	private static final ExecutorService executor = Executors
			.newFixedThreadPool(2);
	
	private static final class ProgressMonitor implements Runnable {

		private void endProgress() {
			progress = 100;
			handler.sendEmptyMessage(WHAT_END);
		}

		private void initProgress() {
			progress = 0;
		}

		public void run() {
			try {
				if (Debug.DEBUG) {
					Log.d(TAG, "ProgressMonitor folder: " + file);
				}
				cancelIfRunning(false);
				
				result = executor.submit(lister);

				initProgress();
				//
				boolean started = false;
				final long timeout = 500;
				final long maxtime = 500 * 2 * 60 * 1; // 1 min or 5?
				long elapsed = 0;
				while (!result.isDone() && !result.isCancelled()) {
					try {
						result.get(timeout, TimeUnit.MILLISECONDS);
					} catch (TimeoutException e) {
						// only show progress after at least x ms
						// e.printStackTrace();
						//
						Log.d(TAG, "timeout: " + elapsed);
						if (!started) {
							started = true;
							startProgress();
						}
						elapsed += timeout;
					}
					if (elapsed > maxtime) {
						Log.d(TAG, "max time exceeded. canceling... "
								+ elapsed);
						result.cancel(true);
						break;
					}
					sendProgress();
				}
				//
				sendProgress();

				if (Debug.DEBUG) {
					Log.d(TAG, "ProgressMonitor done folder: " + file);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				endProgress();
			}
		}

		private void sendProgress() {
			progress += 10;
			progress = (progress < 100 ? progress : 99); // never complete
			//
			handler.sendEmptyMessage(WHAT_PROGRRESS);
		}

		private void startProgress() {
			handler.sendEmptyMessage(WHAT_START);
		}
	}

	private static final Comparator<File> comparator = new Comparator<File>() {
		public int compare(File file1, File file2) {
			boolean isd1 = file1.isDirectory();
			boolean isd2 = file2.isDirectory();
			if (isd1 && !isd2) {
				return -1;
			}
			if (!isd1 && isd2) {
				return 1;
			}
			String k1 = file1.getSortKey();
			String k2 = file2.getSortKey();
			if (k1 == null) {
				return -1;
			}
			if (k2 == null) {
				return 1;
			}
			return k1.compareTo(k2);
		}
	};

	private class ResourceLauncher implements AdapterView.OnItemClickListener {
		public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
			ResourceInfo ri = (ResourceInfo) parent.getItemAtPosition(pos);
			File f = ri.file;
			launch(f);
		}
	}

	private class ResourceAdapter extends ArrayAdapter<ResourceInfo> {

		private Bitmap[] imagesGroup = new Bitmap[DRAWABLES_GROUP.length];

		private BitmapDrawable[] imagesType = new BitmapDrawable[DRAWABLES_TYPE.length];

		private BitmapDrawable[] imagesFolder = new BitmapDrawable[DRAWABLES_FOLDER.length];

		private Rect mOldBounds = new Rect();

		public ResourceAdapter(Context ctx) {
			super(ctx, 0);
			//
			try {
				Resources res = ctx.getResources();
				initImages(res, DRAWABLES_GROUP, imagesGroup);
				initImages(res, DRAWABLES_TYPE, imagesType);
				initImages(res, DRAWABLES_FOLDER, imagesFolder);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		private void initImages(Resources res, int[] drawables,
				BitmapDrawable[] images) {
			for (int i = 0; i < drawables.length; i++) {
				if (drawables[i] == -1) {
					images[i] = null;
					continue;
				}
				images[i] = new BitmapDrawable(BitmapFactory.decodeResource(
						res, drawables[i]));
			}
		}

		private void initImages(Resources res, int[] drawables, Bitmap[] images) {
			for (int i = 0; i < drawables.length; i++) {
				if (drawables[i] == -1) {
					images[i] = null;
					continue;
				}
				images[i] = BitmapFactory.decodeResource(res, drawables[i]);
			}
		}

		private Drawable matchIcon(ResourceInfo info) {
			File f = info.file;
			boolean open = info.selected;
			boolean top = info.top;
			Object obj = f.getIcon();
			if (obj != null) {
				if (obj instanceof Bitmap) {
					Bitmap bm = (Bitmap) obj;
					return new BitmapDrawable(bm);
				} else if (obj instanceof Drawable) {
					Drawable dr = (Drawable) obj;
					return dr;
				} else if (obj instanceof String) {
					String s = (String) obj;
					if (s.startsWith("/icon/")) {
						String pkg = s.substring(6);
						PackageManager pm = getPackageManager();
						try {
							return pm.getApplicationIcon(pkg);
						} catch (NameNotFoundException e) {
							e.printStackTrace();
						}
					}
				}
			}
			//
			boolean dir = f.isDirectory();
			if (dir) {
				boolean root = f.isRoot();
				if (root) {
					return (open ? imagesFolder[1] : imagesFolder[0]);
				}
				if (top) {
					return (open ? imagesFolder[3] : imagesFolder[2]);
				} else {
					return imagesFolder[4]; // x_folder
				}

			}
			// files
			String ct = f.getContentType();
			int len = MIME_TYPE.length;
			for (int i = 0; i < len; i++) {
				if (ct.startsWith(MIME_TYPE[i])) {
					return imagesType[i];
				}
			}
			return imagesType[0]; // app
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			final ResourceInfo info = getItem(position);

			if (convertView == null) {
				final LayoutInflater inflater = getLayoutInflater();
				convertView = inflater.inflate(R.layout.application, parent,
						false);
			}
			Drawable icon = info.icon;
			if (icon == null) {
				icon = matchIcon(info);
				info.iconFiltered = true;
			}
			if (!info.iconFiltered) {
				Bitmap deco = imagesGroup[info.group];
				int width = 48; // 
				int height = 48; // 

				final int iconWidth = icon.getIntrinsicWidth();
				final int iconHeight = icon.getIntrinsicHeight();

				if (icon instanceof PaintDrawable) {
					PaintDrawable painter = (PaintDrawable) icon;
					painter.setIntrinsicWidth(width);
					painter.setIntrinsicHeight(height);
				}

				if (width > 0 && height > 0
						&& (width < iconWidth || height < iconHeight)) {
					final float ratio = (float) iconWidth / iconHeight;

					if (iconWidth > iconHeight) {
						height = (int) (width / ratio);
					} else if (iconHeight > iconWidth) {
						width = (int) (height * ratio);
					}

					final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
							: Bitmap.Config.RGB_565;

					final Bitmap thumb = Bitmap.createBitmap(width, height, c);
					final Canvas canvas = new Canvas(thumb);
					canvas.setDrawFilter(new PaintFlagsDrawFilter(
							Paint.DITHER_FLAG, 0));
					// Copy the old bounds to restore them later
					// If we were to do oldBounds = icon.getBounds(),
					// the call to setBounds() that follows would
					// change the same instance and we would lose the
					// old bounds
					mOldBounds.set(icon.getBounds());
					icon.setBounds(0, 0, width, height);
					icon.draw(canvas);
					icon.setBounds(mOldBounds);
					//
					if (deco != null) {
						canvas.drawBitmap(deco, new Matrix(), null);
					}
					//
					icon = info.icon = new BitmapDrawable(thumb);
					info.iconFiltered = true;
				}
			}

			final TextView tv = (TextView) convertView.findViewById(R.id.label);
			tv.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);

			tv.setText(info.title);
			//
			return convertView;
		}
	}

	private class ApplicationsIntentReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (Debug.DEBUG) {
				Log.d(TAG, "ApplicationsIntentReceiver onReceive:"
								+ intent);
			}
			// TODO blindly reload?
			reload();
		}
	}

	/**
	 * Hides the applications grid when the layout animation is over.
	 */
	private class HideGrid implements Animation.AnimationListener {
		public void onAnimationEnd(Animation animation) {
			mBlockAnimation = false;
		}

		public void onAnimationRepeat(Animation animation) {
		}

		public void onAnimationStart(Animation animation) {
		}
	}

	private class InitTask extends AsyncTask<Void, Void, Void> {

		@Override
		protected Void doInBackground(Void... params) {
			try {
				loadInit();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onCancelled() {
			showBusy(false);
		}

		@Override
		protected void onPostExecute(Void result) {
			try {
				postInit();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				showBusy(false);
			}
		}

		@Override
		protected void onPreExecute() {
			showBusy(true);
		}
	}

	private class OnGestureAdapter implements OnGestureListener {

		public boolean onDown(MotionEvent e) {
			return true;
		}

		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			try {
				if (isLoading()) {
					say(TTS_LOADING);
					return true;
				}
				// if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
				// return false;
				// }
				//
				//
				float dx12 = (e1.getX() - e2.getX());
				float dx21 = (e2.getX() - e1.getX());
				float dy12 = (e1.getY() - e2.getY());
				float dy21 = (e2.getY() - e1.getY());

				float dd = (Math.abs(dx12) - Math.abs(dy12));

				if (dd > 0) {
					// left right
					if (dx12 > SWIPE_MIN_X_DISTANCE
							&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
						doSwipeLeft();
					} else if (dx21 > SWIPE_MIN_X_DISTANCE
							&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
						doSwipeRight();
					}
				} else {
					// up down
					if (dy12 > SWIPE_MIN_Y_DISTANCE
							&& Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
						doSwipeUp();
					} else if (dy21 > SWIPE_MIN_Y_DISTANCE
							&& Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
						doSwipeDown();
					}
				}

				// if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
				// && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
				// doSwipeLeft();
				// } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
				// && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
				// doSwipeRight();
				// } else if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE
				// && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
				// doSwipeUp();
				// } else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE
				// && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
				// doSwipeDown();
				// }
			} catch (Exception e) {
				// nothing
			}

			return true;
		}

		public void onLongPress(MotionEvent e) {
			doLongPress();
		}

		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			return true;
		}

		public void onShowPress(MotionEvent e) {
		}

		public boolean onSingleTapUp(MotionEvent e) {
			doTap();
			return true;
		}

	}

	private class OnInitAdapter implements TextToSpeech.OnInitListener {

		public void onInit(int status) {
			if (status == TextToSpeech.SUCCESS) {
				int result = tts.setLanguage(Locale.US);
				// Try this someday for some interesting results.
				// int result mTts.setLanguage(Locale.FRANCE);
				if (result == TextToSpeech.LANG_MISSING_DATA
						|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
					Log.e(TAG, "Language is not available.");
				}
			} else {
				Log.e(TAG, "Could not initialize TextToSpeech.");
			}
		}

	}

	private class SetBGTask extends AsyncTask<Void, Void, Void> {

		@Override
		protected Void doInBackground(Void... arg0) {
			return null;
		}

		private void load() {
			Drawable wallpaper = peekWallpaper();
			if (wallpaper == null) {
				try {
					clearWallpaper();
				} catch (IOException e) {
					Log.e(TAG, "Failed to clear wallpaper " + e);
				}
			} else {
				getWindow().setBackgroundDrawable(
						new ClippedDrawable(wallpaper));
			}
		}

		@Override
		protected void onPostExecute(Void result) {
			try {
				load();
			} catch (Exception e) {
				// ignore
			}
		}
	}

	/**
	 * Shows the applications grid when the layout animation is over.
	 */
	private class ShowGrid implements Animation.AnimationListener {
		public void onAnimationEnd(Animation animation) {
			mBlockAnimation = false;
			// ViewDebug.stopHierarchyTracing();
		}

		public void onAnimationRepeat(Animation animation) {
		}

		public void onAnimationStart(Animation animation) {
		}
	}

	private class WallpaperIntentReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			if (Debug.DEBUG) {
				Log.d(TAG, "WallpaperIntentReceiver onReceive:" + intent);
			}
			getWindow().setBackgroundDrawable(
					new ClippedDrawable(getWallpaper()));
		}
	}

	private class MountIntentReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context ctx, Intent intent) {
			if (Debug.DEBUG) {
				Log.d(TAG, "MountIntentReceiver onReceive:" + intent);
			}
			String action = intent.getAction();
			if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) {
				if (mHeader != null) {
					mHeader.setText(R.string.msg_sdcard_unmounted);
				}
			} else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
				if (mHeader != null) {
					mHeader.setText(R.string.msg_sdcard_mounted);
				}
			} else {
				//
			}
			reload();
		}
	}

	private static final int[] DRAWABLES_GROUP = { R.drawable.recent,
			R.drawable.superstar, R.drawable.fave, -1 /* all */, };

	private static final int[] DRAWABLES_TYPE = { R.drawable.x_app,
			R.drawable.x_audio, R.drawable.x_html, R.drawable.x_image,
			R.drawable.x_text, R.drawable.x_video, };

	private static final int[] DRAWABLES_FOLDER = { R.drawable.ic_top,
			R.drawable.ic_top, R.drawable.ic_bc32, R.drawable.ic_bc32,
			R.drawable.x_folder, R.drawable.x_link };

	private static final String[] MIME_TYPE = { "application/", "audio/",
			"text/html", "image/", "text/", "video/", };

	private static final String KEY_SAVE_GRID_OPENED = "grid.opened";

	//
	// private static final int SWIPE_MAX_OFF_PATH = 250;

	// private static final int SWIPE_MIN_DISTANCE = 120;

	private static final float SWIPE_MIN_X_DISTANCE = 120;

	private static final float SWIPE_MIN_Y_DISTANCE = 80;

	private static final int SWIPE_THRESHOLD_VELOCITY = 180; // 200

	//
	protected static ResourceAdapter adapter;

	private static GestureDetector gd;

	private static InitTask initTask;

	//private static boolean longPressed;

	private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();

	private boolean mBlockAnimation;
	
	protected static GridView mGrid;

	private Animation mGridEntry;
	private Animation mGridExit;
	
	protected static TextView mHeader;

	private LayoutAnimationController mHideLayoutAnimation;


	private LayoutAnimationController mShowLayoutAnimation;

	private final BroadcastReceiver mWallpaperReceiver = new WallpaperIntentReceiver();

	private final BroadcastReceiver mMountReceiver = new MountIntentReceiver();

	private static File file; 

	private static File[] childs;

	private static int index; // to childs or siblings

	private static int tapCount;

	private TextToSpeech tts;
	private String TTS_HOME;
	private String TTS_ERROR;
	private String TTS_FLING_OFF;
	private String TTS_FLING_ON;
	private String TTS_LOADING_CANCELED;
	private String TTS_LOADING;

	private boolean initing = false;

	protected static View mFlingView;

	protected WebView mFlingViewPage;

	protected ImageView mFlingViewImage;

	private boolean enableTTS;

	private static Future<?> monitor;

	private static AsyncTask<Void, Void, Void> bgTask;

	private static File root;

	private static TextView mBusy;

	private void bindButtons() {
//		mStartButton.setOnClickListener(this);
//		mShowButton.setOnClickListener(this);
//		mQuickViButton.setOnClickListener(this);
		
		ResourceLauncher l = new ResourceLauncher();
		mGrid.setOnItemClickListener(l);
		registerForContextMenu(mGrid);
		//
		//registerForContextMenu(mFlingViewText);
		//registerForContextMenu(mFlingViewImage);
	}

	private static void cancelIfRunning(boolean clear) {
		if (clear) {
			//only in UI thread
			if (mHeader != null) {
				mHeader.setText("");
			}
			showBusy(false);
		}
		try {
			if (monitor != null && !monitor.isDone()) {
				monitor.cancel(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			if (result != null && !result.isDone()) {
				result.cancel(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected boolean isLoading() {
		return (progress < 100);
	}

	protected static void showBusy(boolean b) {
		mBusy.setVisibility(b ? View.VISIBLE : View.GONE);
		mBusy.invalidate();
	}

	private void loadInit() {
		root = new File("v:/");
		File f = readPath();
		file = (f == null ? root : f);
		// load done in postInit
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			switch (event.getKeyCode()) {
			case KeyEvent.KEYCODE_BACK:
				doBack();
				return true;
			case KeyEvent.KEYCODE_HOME:
				return true;
			}
		}
		return super.dispatchKeyEvent(event);
	}

	private void doLongPress() {
		if (Debug.DEBUG) {
			Log.d(TAG, "doLongPress");
		}
		doBack();
	}

	private void doBack() {
		File p = (File) file.getParentFile();
		p = (p == null ? root : p);
		changeTo(p, true);
	}

	private void changeTo(File p, boolean back) {
		//return only if back is true
		if (Debug.DEBUG) {
			Log.d(TAG, "change to: " + p + " back: " + back);
		}
		
		cancelIfRunning(true);

		if (isLoading()) {
			say(TTS_LOADING_CANCELED);
			if (back) {
				return;
			}
		}
		
		if (p == null) {
			return;
		}
		//
		load(p);
		//
		
		say(p);
	}

	private void doSwipeDown() {
		tapCount = 0;
		//
		File[] fl = (childs);
		boolean n = (fl == null || fl.length == 0);
		if (n) {
			say(file);
			return;
		}
		int max = fl.length - 1;
		index = (index < 0 ? 0 : index);
		index = (index > max ? max: index);
		//
		int skip = (max - index) / 2 + 1;
		index += skip;
		index = (index > max ? max: index);
		say(fl[index]);
	}

	private void doSwipeLeft() {
		tapCount = 0;
		//
		File[] fl = (childs);
		boolean n = (fl == null || fl.length == 0);
		if (n) {
			say(file);
			return;
		}
		int max = fl.length - 1;
		index = (index < 0 ? 0 : index);
		index = (index > max ? max: index);
		//
		index--;
		index = (index < 0 ? 0 : index);
		say(fl[index]);
	}

	private void doSwipeRight() {
		tapCount = 0;
		//
		File[] fl = (childs);
		boolean n = (fl == null || fl.length == 0);
		if (n) {
			say(file);
			return;
		}
		int max = fl.length - 1;
		index = (index < 0 ? 0 : index);
		index = (index > max ? max: index);
		//
		index++;
		index = (index > max ? max : index);
		say(fl[index]);
	}

	private void doSwipeUp() {
		tapCount = 0;
		//
		File[] fl = (childs);
		boolean n = (fl == null || fl.length == 0);
		if (n) {
			say(file);
			return;
		}
		int max = fl.length - 1;
		index = (index < 0 ? 0 : index);
		index = (index > max ? max: index);
		//
		int skip = index / 2 + 1;
		index -= skip;
		index = (index < 0 ? 0 : index);
		say(fl[index]);
	}

	private void doTap() {
		if (Debug.DEBUG) {
			Log.d(TAG, "doTap file: " + file +" index: " + index);
		}
		try {
			tapCount++;
			File f = (childs == null || index >= childs.length || index < 0 ? file : childs[index]);	
			file = f;
			if (tapCount > 1) {
				tapCount = 0;
				index = 0;
				//
				launch(f);
			} else {
				saySelected(f);
			}
		
		} catch (Exception e) {
			e.printStackTrace();
			//
			sayError(e);
		} finally {
		}
	}

	private void launch(File f) {
		if (Debug.DEBUG) {
			Log.d(TAG, "launching file: " + f);
		}
		
		cancelIfRunning(true);
		
		if (f.isDirectory()) {
			f.refresh();
			//
			load(f);
			if (isFlinging()) {
				String n = f.getPhoneticName();
				say(getString(R.string.tts_x_opened, n));
			}
		} else {
			savePath(f);
			if (isFlinging()) {
				String s = getString(R.string.tts_start_x, f.getPhoneticName());
				say(s);
			}
			if (f.canExecute()) {
				f.execute();
			} else {
				startViewActivity(f);
			}
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		initing = false;
		//
		savePath(file);
		//Preferences.putFlingMode(this, flingMode);
		//
		cancelIfRunning(true);
	}

	private void startViewActivity(File file) {
		ApplicationUtils.startViewActivity(this, file);
	}

	private void hideApplications() {
		if (mBlockAnimation) {
			return;
		}
		mBlockAnimation = true;
		//
		if (mHideLayoutAnimation == null) {
			mHideLayoutAnimation = AnimationUtils.loadLayoutAnimation(this,
					R.anim.hide_applications);
		}

		mGridExit.setAnimationListener(new HideGrid());
		mGrid.startAnimation(mGridExit);
		mGrid.setVisibility(View.INVISIBLE);

		// mShowApplications.requestFocus();

		// This enables a layout animation; if you uncomment this code, you need
		// to
		// comment the line mGrid.startAnimation() above
		// mGrid.setLayoutAnimationListener(new HideGrid());
		// mGrid.setLayoutAnimation(mHideLayoutAnimation);
		// mGrid.startLayoutAnimation();
	}

	protected void init(Bundle savedInstance) {
		if (Debug.DEBUG) {
			Log.d(TAG, "init: " + savedInstance);
		}
		try {
			initRes();
			initTtsMessages();
			initAdapter();
			//
			initFling();
			//
			bindButtons();
			registerIntentReceivers();
			//
			initTask();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	protected void initTask() {
		if (initTask != null) {
			initTask.cancel(true);
		}
		initTask = new InitTask();
		initTask.execute();
	}

	protected void initAdapter() {
		if (Debug.DEBUG) {
			Log.d(TAG, "initAdapter");
		}
		//resources = new ArrayList<ResourceInfo>();
		adapter = new ResourceAdapter(this);
		mGrid.setAdapter(adapter);
	}

	protected void initFling() {
		if (Debug.DEBUG) {
			Log.d(TAG, "initFling");
		}
		tts = new TextToSpeech(this, new OnInitAdapter());
		gd = new GestureDetector(new OnGestureAdapter());
	}

	protected void initRes() {
		if (Debug.DEBUG) {
			Log.d(TAG, "initRes");
		}
		mGrid = (GridView) findViewById(R.id.thumbnail_view);
		mFlingView = findViewById(R.id.fling_view);
		mFlingViewPage = (WebView) findViewById(R.id.fling_view_page);
		mFlingViewImage = (ImageView) findViewById(R.id.fling_view_image);

		// visual prompt
		mHeader = (TextView) findViewById(R.id.header);
		mHeader.setText("");
		
		// busy indicator
		mBusy = (TextView) findViewById(R.id.busy_indicator);
		mBusy.setVisibility(View.GONE);
		//
		mGridEntry = AnimationUtils.loadAnimation(this, R.anim.grid_entry);
		mGridExit = AnimationUtils.loadAnimation(this, R.anim.grid_exit);
		//
	}

	protected void initTtsMessages() {
		if (Debug.DEBUG) {
			Log.d(TAG, "initTtsMessages");
		}
		TTS_FLING_OFF = getString(R.string.tts_fling_off);
		TTS_FLING_ON = getString(R.string.tts_fling_on);
		TTS_HOME = getString(R.string.tts_home);
		TTS_ERROR = getString(R.string.tts_error);
		TTS_LOADING = getString(R.string.tts_loading);
		TTS_LOADING_CANCELED = getString(R.string.tts_loading_canceled);
	}

	protected boolean isFlinging() {
		return false;
	}

	public void onClick(View v) {
//		final int id = v.getId();
//		switch (id) {
//		case R.id.start:
//			AppHelper.startStartMenuActivity(this);
//			break;
//		case R.id.quickbar_vi:
//			AppHelper.startViActivity(this);
//			break;
//		}
	}

	@Override
	public void onCreate(Bundle savedInstance) {
		super.onCreate(savedInstance);
		
		long start = 0;
		if (Debug.DEBUG) {
			start = System.currentTimeMillis();
			Log.d(TAG, "onCreate: " + savedInstance);
		}
		//
		//setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
		setContentView(R.layout.home);
		setDefaultWallpaper();
		//
		//
		initing = true;
		init(savedInstance);
		//
		if (Debug.DEBUG) {
			long end = System.currentTimeMillis();
			Log.d(TAG, "onCreate: elapsed: " + (end - start) + " start: "
					+ start + " end: " + end);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public void onDestroy() {
		try {
			//
			// Remove the callback for the cached drawables or we leak
			// the previous Home screen on orientation change
			// final int count = mApplications.size();
			// for (int i = 0; i < count; i++) {
			// mApplications.get(i).icon.setCallback(null);
			// }
			unregisterReceiver(mMountReceiver);
			unregisterReceiver(mWallpaperReceiver);
			unregisterReceiver(mApplicationsReceiver);
			//
			if (tts != null) {
				tts.stop();
				tts.shutdown();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		//
		super.onDestroy();
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);

		// Close the menu
		if (Intent.ACTION_MAIN.equals(intent.getAction())) {
			getWindow().closeAllPanels();
		}
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onRestoreInstanceState(Bundle state) {
		super.onRestoreInstanceState(state);
		final boolean opened = state.getBoolean(KEY_SAVE_GRID_OPENED, false);
		if (opened) {
			showApplications(false);
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		//
		enableTTS = Preferences.ttsEnabled(this);
		
		if (Debug.DEBUG) {
			Log.d(TAG, "onResume: init: " + initing +" tts: " + enableTTS);
		}
		
		if (!initing) {
			File f = readPath();
			changeTo(f, false);
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putBoolean(KEY_SAVE_GRID_OPENED,
				mGrid.getVisibility() == View.VISIBLE);
	}

	@Override
	public boolean onTouchEvent(MotionEvent e) {
		if (isFlinging()) {
			return gd.onTouchEvent(e);
		} else {
			return super.onTouchEvent(e);
		}
	}

	/**
	 * Registers various intent receivers. The current implementation registers
	 * only a wallpaper intent receiver to let other applications change the
	 * wallpaper.
	 */
	private void registerIntentReceivers() {
		IntentFilter filter = null;
		//
		filter = new IntentFilter();
		filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
		registerReceiver(mWallpaperReceiver, filter);
		//
		filter = new IntentFilter();
		filter.addAction(Intent.ACTION_PACKAGE_ADDED);
		filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
		filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
		filter.addDataScheme("package");
		registerReceiver(mApplicationsReceiver, filter);
		//
		filter = new IntentFilter();
		filter.addAction(jvs.vfs.opt.Intents.ACTION_VFS_MOUNT_COMPLETED);
		filter.addDataScheme("v");
		filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
		filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
		filter.addDataScheme("file");
		registerReceiver(mMountReceiver, filter);
	}

	private void reload() {
		if (file != null) {
			file.refresh();
			load(file);
		}
	}
	
	private void load(File nf) {
		tapCount = 0;
		index = 0;
		//
		file = nf;
		if (Debug.DEBUG) {
			Log.d(TAG, "load file: " + nf);
		}
		if (nf.isDirectory()) {
			try {
				monitor = executor.submit(progressMonitor);
			} catch (Exception e) {
				onError(e);
			} finally {
			}
		}
	}

	private static void load2() {
		if (Debug.DEBUG) {
			Log.d(TAG, "load2");
		}
		File nf = file;
		File p = (x.java.io.File) nf.getParentFile();
		
		if (childs != null) {
			Arrays.sort(childs, comparator);
		}
		//
		adapter.clear();
		//
		ResourceInfo top = new ResourceInfo();
		top.file = root;
		top.title = "v:/";
		top.top = true;
		adapter.add(top);
		//
		if (!nf.isRoot()) {
			ArrayList<File> parents = new ArrayList<File>();
			while (p != null) {
				parents.add(0, p);
				p = (x.java.io.File) p.getParentFile();
			}
			int len = parents.size();
			// skip root
			for (int i = 1; i < len; i++) {
				File f = parents.get(i);
				ResourceInfo ri = new ResourceInfo();
				ri.top = true;
				ri.file = f;
				ri.title = f.getDisplayName();
				adapter.add(ri);
			}
			//
			ResourceInfo self = new ResourceInfo();
			self.title = nf.getDisplayName();
			self.selected = true;
			self.file = nf;
			self.top = true;
			adapter.add(self);
		}
		//
		File[] cl = childs;
		int len = (cl == null ? 0 : cl.length);
		if (Debug.DEBUG) {
			Log.d(TAG, "childs: " + len);
		}
		if (len > 0) {
			for (int i = 0; i < len; i++) {
				ResourceInfo ri = new ResourceInfo();
				File f = cl[i];
				ri.top = false;
				ri.file = f;
				ri.title = f.getDisplayName();
				// ri.icon = f.getImage();
				ri.phonetic = f.getPhoneticName();
				//
				adapter.add(ri);
			}
		}
		//
		adapter.notifyDataSetChanged();
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "adapter: " + adapter);
		}
		
	}

	protected void say(File f) {
		if (f == null) {
			return;
		}
		String s = (f.isRoot() ? TTS_HOME : f.getPhoneticName());
		mHeader.setText(s);
		speak(s);
	}
	
	protected void say(String s) {
		mHeader.setText(s);
		speak(s);
	}

	protected void sayError(Throwable e) {
		say(TTS_ERROR);
		Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
	}

	protected void sayMode() {
		String s = (isFlinging() ? TTS_FLING_ON : TTS_FLING_OFF);
		mHeader.setText("");
		showBusy(false);
		//
		speak(s);
	}

	protected void saySelected(File f) {
		String s = getString(R.string.tts_x_selected, f.getPhoneticName());
		mHeader.setText(s);
		speak(s);
	}

	private void setDefaultWallpaper() {
		if (Debug.DEBUG) {
			Log.d(TAG, "setDefaultWallpaper");
		}
		if (bgTask != null) {
			bgTask.cancel(true);
		}
		bgTask = new SetBGTask();
		bgTask.execute();
	}

	/**
	 * Show all of the applications by playing an animation on the grid.
	 */
	private void showApplications(boolean animate) {
		if (mBlockAnimation) {
			return;
		}
		mBlockAnimation = true;
		//
		//flingMode = Preferences.FLING_THUMBNAIL;
		//
		//mShowButton.setImageResource(R.drawable.view_fling32);

		if (mShowLayoutAnimation == null) {
			mShowLayoutAnimation = AnimationUtils.loadLayoutAnimation(this,
					R.anim.show_applications);
		}

		// This enables a layout animation; if you uncomment this code, you need
		// to
		// comment the line mGrid.startAnimation() below
		// mGrid.setLayoutAnimationListener(new ShowGrid());
		// mGrid.setLayoutAnimation(mShowLayoutAnimation);
		// mGrid.startLayoutAnimation();

		if (animate) {
			mGridEntry.setAnimationListener(new ShowGrid());
			mGrid.startAnimation(mGridEntry);
		}

		mGrid.setVisibility(View.VISIBLE);

		//
		if (!animate) {
			mBlockAnimation = false;
		}

		// ViewDebug.startHierarchyTracing("HomeActivity", mGrid);
	}

	protected void speak(String s) {
		if (enableTTS && isFlinging() && tts != null) {
			tts.speak(s, TextToSpeech.QUEUE_FLUSH, null);
		}
	}

	private void postInit() {
		initing = false;
		//
		reload();
		//
		sayMode();
		//
		if (isFlinging()) {
			hideApplications();
		} else {
			showApplications(true);
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return super.onKeyDown(keyCode, event);
	}

	@Override
	protected File getSelectedFile() {
		return file;
	}

	@Override
	protected File getContextFile(int pos) {
		try {
			ResourceInfo ri = adapter.getItem(pos);
			return ri.file;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
